package com.ebo.synframework.nettybase.codec;

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.log4j.Logger;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;

import com.ebo.synframework.nettybase.protoPool.ReusedProtoBuilder;
import com.ebo.synframework.util.CryptUtil;
import com.google.protobuf.Message;
import com.google.protobuf.Message.Builder;

public class ProtoEncoder extends OneToOneEncoder {
    
    private static Logger logger = Logger.getLogger("synroom");
    
    private static final String RESPOND_STRING = "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\"/></cross-domain-policy>" + '\0';
    
    private final ChannelBuffer buf;
    
    private final LengthOptimizeConfig lengthOptimizeConfig;
    
    public static AtomicInteger statFlow = new AtomicInteger();
    
    public ProtoEncoder() {
    	buf = ChannelBuffers.buffer(RESPOND_STRING.getBytes().length);
        buf.writeBytes(RESPOND_STRING.getBytes());
        this.lengthOptimizeConfig = new LengthOptimizeConfig();
	}
    
    public ProtoEncoder(LengthOptimizeConfig lengthOptimizeConfig) {
    	buf = ChannelBuffers.buffer(RESPOND_STRING.getBytes().length);
        buf.writeBytes(RESPOND_STRING.getBytes());
        this.lengthOptimizeConfig = lengthOptimizeConfig;
    }
    
    @Override
    protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
        //两种情况，一种是请求响应模式中的响应返回，需要加上请求的内容
    	//另一种是后端推送，这时候只有一个对象
    	if(msg instanceof Object[]) {
    		Object[] msgList = (Object[])msg;
    		String serviceAndMethodStr = (String) msgList[0];
//    		String replaceServiceAndMethodName = this.lengthOptimizeConfig.getEncoderServiceNameMap().get(serviceAndMethodStr);
//    		if(replaceServiceAndMethodName != null) {
//    			serviceAndMethodStr = replaceServiceAndMethodName;
//    		}
    		int status = (Integer)msgList[1];
//    		Builder builder = (Builder) msgList[2];
    		int cmdIndex = (Integer)msgList[4];
    		return encode0(serviceAndMethodStr, status, msgList[2], msgList[3], ctx, false, cmdIndex);
        } else if(msg instanceof String) {
            if(msg.equals(RESPOND_STRING)) {
                return buf;
            }
            logger.warn("someone want to send an unsupport msg type:" + msg);
            return ChannelBuffers.buffer(0);
        }
        return null;
    }

	public ChannelBuffer encode0(String serviceAndMethodStr, int status, Object builder, Object password, ChannelHandlerContext ctx, boolean isSkipMsgLength, int cmdIndex) {
    	try {
    		String replaceServiceAndMethodName = this.lengthOptimizeConfig.getEncoderServiceNameMap().get(serviceAndMethodStr);
    		if(replaceServiceAndMethodName != null) {
    			serviceAndMethodStr = replaceServiceAndMethodName;
    		}
    		byte[] serviceAndMethodStrBytes = serviceAndMethodStr.getBytes();
    		Message proto = null;
    		if(builder instanceof Builder){
    			proto = ((Builder) builder).build();
    		}else if(builder instanceof ReusedProtoBuilder){
    			proto = ((ReusedProtoBuilder) builder).getBuilder().build();
    		}
    		String simpleName = proto.getClass().getSimpleName();
    		String replaceName = lengthOptimizeConfig.getEncoderProtoNameMap().get(simpleName);
    		if(replaceName != null) {
    			simpleName = replaceName;
    		}
    		byte[] simpleClassNameBytes = simpleName.getBytes();
    		byte[] protoBytes = proto.toByteArray();
    		if(status != 2) {
    			protoBytes = CryptUtil.encryptToAes(protoBytes, (String)password, 128);
    		}
    		int classNameBytesLength = simpleClassNameBytes.length;
    		int msgLength = 4 + serviceAndMethodStrBytes.length + 1 + 1 + classNameBytesLength + protoBytes.length;
    		int msgLengthByteLength = 4;
    		if(isSkipMsgLength) {
    			msgLengthByteLength = 0;
    		}
    		ChannelBuffer buf = ChannelBuffers.buffer(msgLengthByteLength + msgLength);
    		if(!isSkipMsgLength) {
    			buf.writeInt(msgLength);
    		}
    		buf.writeInt(cmdIndex);
    		buf.writeByte(serviceAndMethodStrBytes.length);
    		buf.writeBytes(serviceAndMethodStrBytes);
//     		buf.writeByte(status);
    		buf.writeByte(classNameBytesLength);
    		buf.writeBytes(simpleClassNameBytes);
    		buf.writeBytes(protoBytes);
    		if(builder instanceof ReusedProtoBuilder){
    			((ReusedProtoBuilder) builder).release();
    		}
//    		int size = buf.readableBytes() + 42;
//    		statFlow.addAndGet(size);
    		return buf;
    	} catch(Exception e) {
    		e.printStackTrace();
    	}
		return null;
		
	}

}
